<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Content-Language" content="zh-CN"><link href="stylesheet.css" media="all" rel="stylesheet" type="text/css">
<title>日期/时间类型</title>
<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?d286c55b63a3c54a1e43d10d4c203e75"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script>
</head><body class="SECT1">
<div>
<table summary="Header navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><th colspan="5" align="center" valign="bottom">PostgreSQL 8.2.3 中文文档</th></tr>
<tr><td width="10%" align="left" valign="top"><a href="datatype-binary.html" accesskey="P">后退</a></td><td width="10%" align="left" valign="top"><a href="datatype.html">快退</a></td><td width="60%" align="center" valign="bottom">章8. Data Types</td><td width="10%" align="right" valign="top"><a href="datatype.html">快进</a></td><td width="10%" align="right" valign="top"><a href="datatype-boolean.html" accesskey="N">前进</a></td></tr>
</table>
<hr align="LEFT" width="100%"></div>
<div class="SECT1"><h1 class="SECT1"><a name="DATATYPE-DATETIME">8.5. 日期/时间类型</a></h1><a name="AEN4515"></a><a name="AEN4517"></a><a name="AEN4519"></a><a name="AEN4521"></a><a name="AEN4523"></a><a name="AEN4525"></a><a name="AEN4527"></a><a name="AEN4529"></a><a name="AEN4531"></a>
<p><a href="datatype-datetime.html#DATATYPE-DATETIME-TABLE">表8-9</a>显示了 PostgreSQL 支持的 SQL 中所有日期和时间类型。这些数据类型上可以进行的操作在<a href="functions-datetime.html">节9.9</a>中描述。</p>
<div class="TABLE"><a name="DATATYPE-DATETIME-TABLE"></a>
<p><b>表8-9. 日期/时间类型</b></p>
<table border="1" class="CALSTABLE"><col><col><col><col><col><col>
<thead>
<tr><th>名字</th><th>存储空间</th><th>描述</th><th>最低值</th><th>最高值</th><th>分辨率</th></tr>
</thead>
<tbody>
<tr><td><tt class="TYPE">timestamp [ (<tt class="REPLACEABLE"><i>p</i></tt>) ] [ without time zone ]</tt></td><td>8 字节</td><td>日期和时间</td><td>4713 BC</td><td>5874897 AD</td><td>1 毫秒 / 14 位</td></tr>
<tr><td><tt class="TYPE">timestamp [ (<tt class="REPLACEABLE"><i>p</i></tt>) ] with time zone</tt></td><td>8 字节</td><td>日期和时间，带时区</td><td>4713 BC</td><td>5874897 AD</td><td>1 毫秒 / 14 位</td></tr>
<tr><td><tt class="TYPE">interval [ (<tt class="REPLACEABLE"><i>p</i></tt>) ]</tt></td><td>12 字节</td><td>时间间隔</td><td>-178000000 年</td><td>178000000 年</td><td>1 毫秒 / 14 位</td></tr>
<tr><td><tt class="TYPE">date</tt></td><td>4 字节</td><td>只用于日期</td><td>4713 BC</td><td>5874897 AD</td><td>1 天</td></tr>
<tr><td><tt class="TYPE">time [ (<tt class="REPLACEABLE"><i>p</i></tt>) ] [ without time zone ]</tt></td><td>8 字节</td><td>只用于一日内时间</td><td>00:00:00</td><td>24:00:00</td><td>1 毫秒 / 14 位</td></tr>
<tr><td><tt class="TYPE">time [ (<tt class="REPLACEABLE"><i>p</i></tt>) ] with time zone</tt></td><td>12 字节</td><td>只用于一日内时间，带时区</td><td>00:00:00+1459</td><td>24:00:00-1459</td><td>1 毫秒 / 14 位</td></tr>
</tbody>
</table>
</div>
<div class="NOTE">
<blockquote class="NOTE">
<p><b>【注意】</b>在 PostgreSQL 7.3 以前，只写 <tt class="TYPE">timestamp</tt> 等效于 <tt class="TYPE">timestamp with time zone</tt> 。这是和 SQL 不兼容的。</p>
</blockquote>
</div>
<p><tt class="TYPE">time</tt>, <tt class="TYPE">timestamp</tt>, <tt class="TYPE">interval</tt> 接受一个可选的精度值 <tt class="REPLACEABLE"><i>p</i></tt> 以指明秒域中小数部分的位数。没有明确的缺省精度，<tt class="REPLACEABLE"><i>p</i></tt> 的范围对 <tt class="TYPE">timestamp</tt> 和 <tt class="TYPE">interval</tt> 类型是从 0 到大约 6 。</p>
<div class="NOTE">
<blockquote class="NOTE">
<p><b>【注意】</b>如果 <tt class="TYPE">timestamp</tt> 数值是以双精度浮点数(目前的缺省)的方式存储的，那么有效精度会小于 6 。<tt class="TYPE">timestamp</tt> 值是以 2000-01-01 午夜之前或之后的秒数存储的，而微秒的精度是为那些在 2000-01-01 前后几年的日期实现的，对于那些远一些的日子，精度会下降。如果<tt class="TYPE">timestamp</tt> 以八字节整数存储(一个编译时的选项)，那么微秒的精度就可以在数值的全部范围内都可以获得。不过，八位整数的时间戳范围缩小到 4713 BC 到 294276 AD 之间。同一个编译时选项也决定 <tt class="TYPE">time</tt> 和 <tt class="TYPE">interval</tt> 值是保存成浮点数还是八字节整数。在以浮点数存储的时候，随着时间间隔的增加，大的 <tt class="TYPE">interval</tt> 数值的精度会降低。</p>
</blockquote>
</div>
<p>对于 <tt class="TYPE">time</tt> 类型，如果使用了八字节的整数存储，那么 <tt class="REPLACEABLE"><i>p</i></tt> 允许的范围是从 0 到 6 ，如果使用的是浮点数存储，那么这个范围是 0 到 10 。</p>
<p><tt class="TYPE">time with time zone</tt> 类型是 SQL 标准定义的，但是完整定义的有些方面会导致有问题的用法。在大多数情况下，<tt class="TYPE">date</tt>, <tt class="TYPE">time</tt>, <tt class="TYPE">timestamp without time zone</tt>, <tt class="TYPE">timestamp with time zone</tt> 的组合就应该能提供一切应用需要的日期/时间的完整功能。</p>
<p><tt class="TYPE">abstime</tt> 和 <tt class="TYPE">reltime</tt> 类型是低分辨率类型，它们被用于系统内部。我们反对你使用这些类型，因为这些旧类型的部分或全部可能会在未来的版本里消失。</p>
<div class="SECT2"><h2 class="SECT2"><a name="DATATYPE-DATETIME-INPUT">8.5.1. 日期/时间输入</a></h2>
<p>日期和时间的输入几乎可以是任何合理的格式，包括 ISO-8601 格式、SQL-兼容格式、传统 POSTGRES 格式、其它的形式。对于一些格式，日期输入里的月和日可能会让人迷惑，因此系统支持自定义这些字段的顺序。把 <a href="runtime-config-client.html#GUC-DATESTYLE">DateStyle</a> 参数设置为 <tt class="LITERAL">MDY</tt> 就按照"月-日-年"解析，设置为 <tt class="LITERAL">DMY</tt> 就按照"日-月-年"解析，设置为 <tt class="LITERAL">YMD</tt> 就按照"年-月-日"解析。</p>
<p>PostgreSQL 在处理日期/时间输入上比 SQL 标准要求的更灵活。参阅<a href="datetime-appendix.html">附录B</a>获取关于日期/时间输入的准确分析规则和可识别文本字段，包括月份、星期几、时区。</p>
<p>请记住任何日期或者时间的文本输入需要由单引号包围，就像一个文本字符串一样。参考<a href="sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS-GENERIC">节4.1.2.5</a>获取更多信息。SQL 要求使用下面的语法：</p>
<pre class="SYNOPSIS"><tt class="REPLACEABLE"><i>type</i></tt> [ (<tt class="REPLACEABLE"><i>p</i></tt>) ] '<tt class="REPLACEABLE"><i>value</i></tt>'</pre>
<p>可选的精度声明中的 <tt class="REPLACEABLE"><i>p</i></tt> 是一个整数，表示在秒域中小数部分的位数，我们可以对 <tt class="TYPE">time</tt>, <tt class="TYPE">timestamp</tt>, <tt class="TYPE">interval</tt> 类型声明精度。允许的精度在上面已经说明。如果在常量声明中没有声明精度，缺省是文本值的精度。</p>
<div class="SECT3"><h3 class="SECT3"><a name="AEN4660">8.5.1.1. 日期</a></h3><a name="AEN4662"></a>
<p><a href="datatype-datetime.html#DATATYPE-DATETIME-DATE-TABLE">表8-10</a>显示了 <tt class="TYPE">date</tt> 类型可能的输入方式。</p>
<div class="TABLE"><a name="DATATYPE-DATETIME-DATE-TABLE"></a>
<p><b>表8-10. 日期输入</b></p>
<table border="1" class="CALSTABLE"><col><col>
<thead>
<tr><th>例子</th><th>描述</th></tr>
</thead>
<tbody>
<tr><td>January 8, 1999</td><td>在任何 <a href="runtime-config-client.html#GUC-DATESTYLE">DateStyle</a> 输入模式下都无歧义</td></tr>
<tr><td>1999-01-08</td><td>ISO 8601 格式(建议格式)，任何方式下都是 1999 年 1 月 8 号</td></tr>
<tr><td>1/8/1999</td><td>有歧义，在 <tt class="LITERAL">MDY</tt> 下是一月八号；在 <tt class="LITERAL">DMY</tt> 模式下是八月一日</td></tr>
<tr><td>1/18/1999</td><td><tt class="LITERAL">MDY</tt> 模式下是一月十八日，其它模式下被拒绝</td></tr>
<tr><td>01/02/03</td><td><tt class="LITERAL">MDY</tt> 模式下的 2003 年 1 月 2 日；<tt class="LITERAL">DMY</tt> 模式下的 2003 年 2 月 1 日；<tt class="LITERAL">YMD</tt> 模式下的 2001 年 2 月 3 日</td></tr>
<tr><td>1999-Jan-08</td><td>任何模式下都是 1 月 8 日</td></tr>
<tr><td>Jan-08-1999</td><td>任何模式下都是 1 月 8 日</td></tr>
<tr><td>08-Jan-1999</td><td>任何模式下都是 1 月 8 日</td></tr>
<tr><td>99-Jan-08</td><td><tt class="LITERAL">YMD</tt> 模式下是 1 月 8 日，否则错误</td></tr>
<tr><td>08-Jan-99</td><td>一月八日，除了在 <tt class="LITERAL">YMD</tt> 模式下是错误的之外</td></tr>
<tr><td>Jan-08-99</td><td>一月八日，除了在 <tt class="LITERAL">YMD</tt> 模式下是错误的之外</td></tr>
<tr><td>19990108</td><td>ISO 8601；任何模式下都是 1999 年 1 月 8 日</td></tr>
<tr><td>990108</td><td>ISO 8601；任何模式下都是 1999 年 1 月 8 日</td></tr>
<tr><td>1999.008</td><td>年和年里的第几天</td></tr>
<tr><td>J2451187</td><td>儒略日</td></tr>
<tr><td>January 8, 99 BC</td><td>公元前 99 年</td></tr>
</tbody>
</table>
</div>
</div>
<div class="SECT3"><h3 class="SECT3"><a name="AEN4733">8.5.1.2. 时间</a></h3><a name="AEN4735"></a><a name="AEN4737"></a><a name="AEN4739"></a>
<p>当日时间类型是 <tt class="TYPE">time [ (<tt class="REPLACEABLE"><i>p</i></tt>) ] without time zone</tt> 和 <tt class="TYPE">time [ (<tt class="REPLACEABLE"><i>p</i></tt>) ] with time zone</tt> 。只写 <tt class="TYPE">time</tt> 等效于 <tt class="TYPE">time without time zone</tt> 。</p>
<p>这些类型的有效输入由当日时间后面跟着可选的时区组成(参阅<a href="datatype-datetime.html#DATATYPE-DATETIME-TIME-TABLE">表8-11</a>和<a href="datatype-datetime.html#DATATYPE-TIMEZONE-TABLE">表8-12</a>)。如果在 <tt class="TYPE">time without time zone</tt> 类型的输入中声明了时区，那么它会被悄悄地忽略。同样指定的日期也会被忽略，除非使用了一个包括夏令时规则的时区名，比如 <tt class="LITERAL">America/New_York</tt>，在这种情况下，必须指定日期以确定这个时间是标准时间还是夏令时。时区偏移将记录在 <tt class="TYPE">time with time zone</tt> 中。</p>
<div class="TABLE"><a name="DATATYPE-DATETIME-TIME-TABLE"></a>
<p><b>表8-11. 时间输入</b></p>
<table border="1" class="CALSTABLE"><col><col>
<thead>
<tr><th>例子</th><th>描述</th></tr>
</thead>
<tbody>
<tr><td><tt class="LITERAL">04:05:06.789</tt></td><td>ISO 8601</td></tr>
<tr><td><tt class="LITERAL">04:05:06</tt></td><td>ISO 8601</td></tr>
<tr><td><tt class="LITERAL">04:05</tt></td><td>ISO 8601</td></tr>
<tr><td><tt class="LITERAL">040506</tt></td><td>ISO 8601</td></tr>
<tr><td><tt class="LITERAL">04:05 AM</tt></td><td>与 04:05 一样；AM 不影响数值</td></tr>
<tr><td><tt class="LITERAL">04:05 PM</tt></td><td>与 16:05 一样；输入小时数必须 &lt;= 12</td></tr>
<tr><td><tt class="LITERAL">04:05:06.789-8</tt></td><td>ISO 8601</td></tr>
<tr><td><tt class="LITERAL">04:05:06-08:00</tt></td><td>ISO 8601</td></tr>
<tr><td><tt class="LITERAL">04:05-08:00</tt></td><td>ISO 8601</td></tr>
<tr><td><tt class="LITERAL">040506-08</tt></td><td>ISO 8601</td></tr>
<tr><td><tt class="LITERAL">04:05:06 PST</tt></td><td>缩写的时区</td></tr>
<tr><td><tt class="LITERAL">2003-04-12 04:05:06 America/New_York</tt></td><td>用名字声明的时区</td></tr>
</tbody>
</table>
</div>
<div class="TABLE"><a name="DATATYPE-TIMEZONE-TABLE"></a>
<p><b>表8-12. 时区输入</b></p>
<table border="1" class="CALSTABLE"><col><col>
<thead>
<tr><th>例子</th><th>描述</th></tr>
</thead>
<tbody>
<tr><td><tt class="LITERAL">PST</tt></td><td>太平洋标准时间(Pacific Standard Time)</td></tr>
<tr><td><tt class="LITERAL">America/New_York</tt></td><td>完整时区名称</td></tr>
<tr><td><tt class="LITERAL">PST8PDT</tt></td><td>POSIX 风格的时区</td></tr>
<tr><td><tt class="LITERAL">-8:00</tt></td><td>ISO-8601 与 PST 的偏移</td></tr>
<tr><td><tt class="LITERAL">-800</tt></td><td>ISO-8601 与 PST 的偏移</td></tr>
<tr><td><tt class="LITERAL">-8</tt></td><td>ISO-8601 与 PST 的偏移</td></tr>
<tr><td><tt class="LITERAL">zulu</tt></td><td>军方对 UTC 的缩写(译注：可能是美军)</td></tr>
<tr><td><tt class="LITERAL">z</tt></td><td><tt class="LITERAL">zulu</tt> 的缩写</td></tr>
</tbody>
</table>
</div>
<p>参考<a href="datatype-datetime.html#DATATYPE-TIMEZONES">节8.5.3</a> 以获取如何指定时区的更多信息。</p>
</div>
<div class="SECT3"><h3 class="SECT3"><a name="AEN4853">8.5.1.3. 时间戳</a></h3><a name="AEN4855"></a><a name="AEN4857"></a><a name="AEN4859"></a>
<p>时间戳类型的有效输入由一个日期和时间的连接组成，后面跟着一个可选的时区，一个可选的 <tt class="LITERAL">AD</tt> 或 <tt class="LITERAL">BC</tt> 。另外，<tt class="LITERAL">AD</tt>/<tt class="LITERAL">BC</tt> 可以出现在时区前面，但这个顺序并非最佳的。因此</p>
<pre class="PROGRAMLISTING">1999-01-08 04:05:06</pre>
<p>和</p>
<pre class="PROGRAMLISTING">1999-01-08 04:05:06 -8:00</pre>
<p>都是有效的数值，它是兼容 ISO-8601 的。另外，也支持下面这种使用广泛的格式</p>
<pre class="PROGRAMLISTING">January 8 04:05:06 1999 PST</pre>
<p>SQL 标准通过"+"或者"-"是否存在来区分 <tt class="TYPE">timestamp without time zone</tt> 和 <tt class="TYPE">timestamp with time zone</tt> 文本。因此，根据标准，</p>
<pre class="PROGRAMLISTING">TIMESTAMP '2004-10-19 10:23:54'</pre>
<p>是一个 <tt class="TYPE">timestamp without time zone</tt> ，而</p>
<pre class="PROGRAMLISTING">TIMESTAMP '2004-10-19 10:23:54+02'</pre>
<p>是一个 <tt class="TYPE">timestamp with time zone</tt> 。PostgreSQL 从来不会在确定文本的类型之前检查文本内容，因此会把上面两个都看做是 <tt class="TYPE">timestamp without time zone</tt> 。因此要保证把上面的第二个当作 <tt class="TYPE">timestamp with time zone</tt> 看待，就要给它明确的类型：</p>
<pre class="PROGRAMLISTING">TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02'</pre>
<p>如果一个文本已被确定是 <tt class="TYPE">timestamp without time zone</tt>，PostgreSQL 将悄悄忽略任何文本中指出的时区。因此，生成的日期/时间值是从输入值的日期/时间字段衍生出来的，并且没有就时区进行调整。</p>
<p>对于 <tt class="TYPE">timestamp with time zone</tt> ，内部存储的数值总是 UTC(全球统一时间，以前也叫格林威治时间 GMT)。如果一个输入值有明确的时区声明，那么它将用该时区合适的偏移量转换成 UTC 。如果在输入字符串里没有时区声明，那么它就假设是在系统的 <a href="runtime-config-client.html#GUC-TIMEZONE">timezone</a> 参数里的那个时区，然后使用这个 <tt class="VARNAME">timezone</tt> 时区转换成 UTC 。</p>
<p>如果输出一个 <tt class="TYPE">timestamp with time zone</tt> ，那么它总是从 UTC 转换成当前的 <tt class="VARNAME">timezone</tt> 时区，并且显示为该时区的本地时间。要看其它时区的该时间，要么修改 <tt class="VARNAME">timezone</tt> ，要么使用 <tt class="LITERAL">AT TIME ZONE</tt> 构造(参阅<a href="functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT">节9.9.3</a>)。</p>
<p>在 <tt class="TYPE">timestamp without time zone</tt> 和 <tt class="TYPE">timestamp with time zone</tt> 之间的转换通常假设 <tt class="TYPE">timestamp without time zone</tt> 数值应该以 <tt class="VARNAME">timezone</tt> 本地时间的形式接受或者写出。其它的时区引用可以用 <tt class="LITERAL">AT TIME ZONE</tt> 的方式为转换声明。</p>
</div>
<div class="SECT3"><h3 class="SECT3"><a name="AEN4903">8.5.1.4. 时间间隔</a></h3><a name="AEN4905"></a>
<p><tt class="TYPE">interval</tt> 数值可以用下面的语法声明：</p>
<pre class="PROGRAMLISTING">[<span class="OPTIONAL">@</span>] <tt class="REPLACEABLE"><i>quantity</i></tt> <tt class="REPLACEABLE"><i>unit</i></tt> [<span class="OPTIONAL"><tt class="REPLACEABLE"><i>quantity</i></tt> <tt class="REPLACEABLE"><i>unit</i></tt>...</span>] [<span class="OPTIONAL"><tt class="REPLACEABLE"><i>direction</i></tt></span>]</pre>
<p>这里：<tt class="REPLACEABLE"><i>quantity</i></tt> 是一个数字(可能有符号)；<tt class="REPLACEABLE"><i>unit</i></tt> 是 <tt class="LITERAL">second</tt>, <tt class="LITERAL">minute</tt>, <tt class="LITERAL">hour</tt>, <tt class="LITERAL">day</tt>, <tt class="LITERAL">week</tt>, <tt class="LITERAL">month</tt>, <tt class="LITERAL">year</tt>, <tt class="LITERAL">decade</tt>, <tt class="LITERAL">century</tt>, <tt class="LITERAL">millennium</tt> 或者这些单位的缩写或复数；<tt class="REPLACEABLE"><i>direction</i></tt> 可以是 <tt class="LITERAL">ago</tt> 或者为空。<tt class="LITERAL">@</tt> 符号是一个可选的东西。不同的单位以及相应正确的符号都是隐含地增加的。</p>
<p>日期、小时、分钟、秒钟的数量可以在无明确单位标记的情况下声明。比如，<tt class="LITERAL">'1 12:59:10'</tt> 和 <tt class="LITERAL">'1 day 12 hours 59 min 10 sec'</tt> 读数一样。</p>
<p>可选的精度 <tt class="REPLACEABLE"><i>p</i></tt> 应该介于 0 和 6 之间，并且缺省是输入文本的精度。</p>
<p>在内部，<tt class="TYPE">interval</tt> 的值按照月、日、秒存储。这样做是恰当的，因为每个月的天数不同，并且每天可能有 23 或 25 小时(涉及夏令时调整时)。因为时间间隔通常是从字符串常量或 <tt class="TYPE">timestamp</tt> 之差创建的，这种存储方法在大多数情况下更加合理。<code class="FUNCTION">justify_days</code> 和 <code class="FUNCTION">justify_hours</code> 函数用于调整溢出的日期和小时。</p>
</div>
<div class="SECT3"><h3 class="SECT3"><a name="AEN4943">8.5.1.5. 特殊值</a></h3><a name="AEN4945"></a><a name="AEN4948"></a>
<p>PostgreSQL 为方便起见支持在<a href="datatype-datetime.html#DATATYPE-DATETIME-SPECIAL-TABLE">表8-13</a>里面显示的几个特殊输入值。值 <tt class="LITERAL">infinity</tt> 和 <tt class="LITERAL">-infinity</tt> 是特别在系统内部表示的，并且将按照同样的方式显示；但是其它的都只是符号缩写，在读取的时候将被转换成普通的日期/时间值。特别是 <tt class="LITERAL">now</tt> 和相关的字符串在读取的时候就被转换成对应的数值。所有这些值在 SQL 命令里当作普通常量对待时，都需要写在单引号里面。</p>
<div class="TABLE"><a name="DATATYPE-DATETIME-SPECIAL-TABLE"></a>
<p><b>表8-13. 特殊日期/时间输入</b></p>
<table border="1" class="CALSTABLE"><col><col>
<thead>
<tr><th>输入字符串</th><th>适用类型</th><th>描述</th></tr>
</thead>
<tbody>
<tr><td><tt class="LITERAL">epoch</tt></td><td><tt class="TYPE">date</tt>, <tt class="TYPE">timestamp</tt></td><td>1970-01-01 00:00:00+00 (Unix 系统零时)</td></tr>
<tr><td><tt class="LITERAL">infinity</tt></td><td><tt class="TYPE">timestamp</tt></td><td>比任何其它时间戳都晚</td></tr>
<tr><td><tt class="LITERAL">-infinity</tt></td><td><tt class="TYPE">timestamp</tt></td><td>比任何其它时间戳都早</td></tr>
<tr><td><tt class="LITERAL">now</tt></td><td><tt class="TYPE">date</tt>, <tt class="TYPE">time</tt>, <tt class="TYPE">timestamp</tt></td><td>当前事务的开始时间</td></tr>
<tr><td><tt class="LITERAL">today</tt></td><td><tt class="TYPE">date</tt>, <tt class="TYPE">timestamp</tt></td><td>今日午夜</td></tr>
<tr><td><tt class="LITERAL">tomorrow</tt></td><td><tt class="TYPE">date</tt>, <tt class="TYPE">timestamp</tt></td><td>明日午夜</td></tr>
<tr><td><tt class="LITERAL">yesterday</tt></td><td><tt class="TYPE">date</tt>, <tt class="TYPE">timestamp</tt></td><td>昨日午夜</td></tr>
<tr><td><tt class="LITERAL">allballs</tt></td><td><tt class="TYPE">time</tt></td><td>00:00:00.00 UTC</td></tr>
</tbody>
</table>
</div>
<p>下列 SQL 兼容函数也可以用于获取对应数据类型的当前时间值：<tt class="LITERAL">CURRENT_DATE</tt>, <tt class="LITERAL">CURRENT_TIME</tt>, <tt class="LITERAL">CURRENT_TIMESTAMP</tt>, <tt class="LITERAL">LOCALTIME</tt>, <tt class="LITERAL">LOCALTIMESTAMP</tt> 。后四个接受一个可选的精度声明(<a href="functions-datetime.html#FUNCTIONS-DATETIME-CURRENT">节9.9.4</a>)。不过，请注意这些 SQL 函数<span class="emphasis"><i class="EMPHASIS">不是</i></span>被当作数据输入字符串识别的。</p>
</div>
</div>
<div class="SECT2"><h2 class="SECT2"><a name="DATATYPE-DATETIME-OUTPUT">8.5.2. 日期/时间输出</a></h2><a name="AEN5031"></a><a name="AEN5035"></a>
<p>日期/时间类型的输出格式可以使用 <tt class="LITERAL">SET datestyle</tt> 设成 ISO 8601(默认)、SQL(Ingres)、传统的 POSTGRES 、German 四种风格之一。SQL 标准要求使用 ISO 8601 格式。"SQL"输出格式的名字是历史偶然。<a href="datatype-datetime.html#DATATYPE-DATETIME-OUTPUT-TABLE">表8-14</a>显示了每种输出风格的例子。<tt class="TYPE">date</tt> 和 <tt class="TYPE">time</tt> 类型的输出当然只是给出的例子里面的日期和时间部分。</p>
<div class="TABLE"><a name="DATATYPE-DATETIME-OUTPUT-TABLE"></a>
<p><b>表8-14. 日期/时间输出风格</b></p>
<table border="1" class="CALSTABLE"><col><col><col>
<thead>
<tr><th>风格</th><th>描述</th><th>例子</th></tr>
</thead>
<tbody>
<tr><td>ISO</td><td>ISO 8601/SQL 标准</td><td>1997-12-17 07:37:16-08</td></tr>
<tr><td>SQL</td><td>传统风格</td><td>12/17/1997 07:37:16.00 PST</td></tr>
<tr><td>POSTGRES</td><td>原始风格</td><td>Wed Dec 17 07:37:16 1997 PST</td></tr>
<tr><td>German</td><td>地区风格</td><td>17.12.1997 07:37:16.00 PST</td></tr>
</tbody>
</table>
</div>
<p>如果声明了 DMY 顺序，那么在 SQL 和 POSTGRES 风格里，日期在月份之前出现，否则月份出现在日期之前(参阅<a href="datatype-datetime.html#DATATYPE-DATETIME-INPUT">节8.5.1</a>看看这个设置如何影响对输入值的解释)。<a href="datatype-datetime.html#DATATYPE-DATETIME-OUTPUT2-TABLE">表8-15</a>显示了一个例子。</p>
<div class="TABLE"><a name="DATATYPE-DATETIME-OUTPUT2-TABLE"></a>
<p><b>表8-15. 日期顺序习惯</b></p>
<table border="1" class="CALSTABLE"><col><col><col>
<thead>
<tr><th><tt class="VARNAME">datestyle</tt> 设置</th><th>输入顺序</th><th>输出样例</th></tr>
</thead>
<tbody>
<tr><td><tt class="LITERAL">SQL, DMY</tt></td><td><tt class="REPLACEABLE"><i>日</i></tt>/<tt class="REPLACEABLE"><i>月</i></tt>/<tt class="REPLACEABLE"><i>年</i></tt></td><td>17/12/1997 15:37:16.00 CET</td></tr>
<tr><td><tt class="LITERAL">SQL, MDY</tt></td><td><tt class="REPLACEABLE"><i>月</i></tt>/<tt class="REPLACEABLE"><i>日</i></tt>/<tt class="REPLACEABLE"><i>年</i></tt></td><td>12/17/1997 07:37:16.00 PST</td></tr>
<tr><td><tt class="LITERAL">Postgres, DMY</tt></td><td><tt class="REPLACEABLE"><i>日</i></tt>/<tt class="REPLACEABLE"><i>月</i></tt>/<tt class="REPLACEABLE"><i>年</i></tt></td><td>Wed 17 Dec 07:37:16 1997 PST</td></tr>
</tbody>
</table>
</div>
<p><tt class="TYPE">interval</tt> 的输出看起来像输入格式，只是像 <tt class="LITERAL">century</tt> 或 <tt class="LITERAL">week</tt> 这样的单位被转换成年和日，而 <tt class="LITERAL">ago</tt> 被转换成合适的符号。在 ISO 模式下输出看起来像</p>
<pre class="PROGRAMLISTING">[<span class="OPTIONAL"><tt class="REPLACEABLE"><i>quantity</i></tt> <tt class="REPLACEABLE"><i>unit</i></tt> [<span class="OPTIONAL">...</span>]</span>] [<span class="OPTIONAL"><tt class="REPLACEABLE"><i>days</i></tt></span>] [<span class="OPTIONAL"><tt class="REPLACEABLE"><i>hours</i></tt>:<tt class="REPLACEABLE"><i>minutes</i></tt>:<tt class="REPLACEABLE"><i>seconds</i></tt></span>]</pre>
<p>用户可以用 <tt class="COMMAND">SET datestyle</tt> 命令选取日期/时间的风格，也可以在配置文件 <tt class="FILENAME">postgresql.conf</tt> 中的 <a href="runtime-config-client.html#GUC-DATESTYLE">DateStyle</a> 参数中设置，或者在服务器或客户端的 <tt class="ENVAR">PGDATESTYLE</tt> 环境变量中设置。我们也可以用格式化函数 <code class="FUNCTION">to_char</code>(参见<a href="functions-formatting.html">节9.8</a>)来更灵活地控制时间/日期地输出。</p>
</div>
<div class="SECT2"><h2 class="SECT2"><a name="DATATYPE-TIMEZONES">8.5.3. 时区</a></h2><a name="AEN5136"></a>
<p>时区和时区习惯不仅仅受地球几何形状的影响，还受到政治决定的影响。到了 19 世纪，全球的时区变得稍微标准化了些，但是还是易于遭受随意的修改，部分是因为夏时制规则。PostgreSQL 目前支持 1902 年到 2038 年之间的夏时制信息(对应于传统 Unix 系统时间的完整跨度)。如果时间超过这个范围，那么假设时间是所选时区的"标准时间"，不管它们落在哪个年份里面。</p>
<p>PostgreSQL 在典型应用中尽可能与 SQL 的定义相兼容。但 SQL 标准在日期/时间类型和功能上有一些奇怪的混淆。两个显而易见的问题是：</p>
<ul>
<li><p><tt class="TYPE">date</tt> 类型与时区没有联系，而 <tt class="TYPE">time</tt> 类型却有或可以有。然而，现实世界的时区只有在与时间和日期都关联时才有意义，因为时间偏移量(时差)可能因为实行类似夏时制这样的制度而在一年里有所变化。</p></li>
<li><p>缺省的时区用一个数字常量表示与 UTC 的偏移(时差)。因此，当跨 DST(夏时制)界限做日期/时间算术时，我们根本不可能把夏时制这样的因素计算进去。</p></li>
</ul>
<p>为了克服这些困难，我们建议在使用时区的时候，使用那些同时包含日期和时间的日期/时间类型。我们建议<span class="emphasis"><i class="EMPHASIS">不要</i></span>使用 <tt class="TYPE">time with time zone</tt> 类型(尽管 PostgreSQL 出于合理应用以及为了与其它 RDBMS 兼容的考虑支持这个类型)。PostgreSQL 假设你用于任何类型的本地时区都只包含日期或时间(而不包含时区)。</p>
<p>在系统内部，所有日期和时间都用全球统一时间 UTC 格式存储，时间在发给客户前端前由数据库服务器根据 <a href="runtime-config-client.html#GUC-TIMEZONE">timezone</a> 配置参数声明的时区转换成本地时间。</p>
<p>PostgreSQL 允许你用三种方法指定时区：</p>
<ul>
<li><p>完整的时区名。例如 <tt class="LITERAL">America/New_York</tt> 。所有可以识别的时区名在 <tt class="LITERAL">pg_timezone_names</tt> 视图中列出(参见<a href="view-pg-timezone-names.html">节43.49</a>)。PostgreSQL 使用广泛使用的 <tt class="LITERAL">zic</tt> 时区数据，所以这些时区名在其它软件里也能被轻松的识别。</p></li>
<li><p>时区缩写。例如 <tt class="LITERAL">PST</tt> 。这种缩写名通常只是定义了相对于 UTC 的偏移量，而前一种完整的时区名可能还隐含着一组夏时制转换规则。所有可以识别的时区缩写在 <tt class="LITERAL">pg_timezone_abbrevs</tt> 视图中列出(参见<a href="view-pg-timezone-abbrevs.html">节43.48</a>)。你不能使用时区缩写来设置 <a href="runtime-config-client.html#GUC-TIMEZONE">timezone</a> 配置参数，但是你可以在日期/时间输入值中结合 <tt class="LITERAL">AT TIME ZONE</tt> 操作符使用时区缩写。</p></li>
<li><p>除完整的时区名及其缩写之外，PostgreSQL 还接受 POSIX 风格的 <tt class="REPLACEABLE"><i>STD</i></tt><tt class="REPLACEABLE"><i>offset</i></tt> 或 <tt class="REPLACEABLE"><i>STD</i></tt><tt class="REPLACEABLE"><i>offset</i></tt><tt class="REPLACEABLE"><i>DST</i></tt> 格式的时区，其中的 <tt class="REPLACEABLE"><i>STD</i></tt> 是时区缩写、<tt class="REPLACEABLE"><i>offset</i></tt> 是一个相对于 UTC 的小时偏移量、<tt class="REPLACEABLE"><i>DST</i></tt> 是一个可选的夏时制时区缩写(假定相对于给定的偏移量提前一小时)。例如，如果 <tt class="LITERAL">EST5EDT</tt> 不是一个已识别的时区名，那么它将等同于美国东部时间，如果存在夏时制时区，那么它将按照美国的时区规则使用，因此这个特性在北美州之外没什么用处。需要提醒的是这个特性会导致悄悄的接受不合理的输入，因为它不对时区缩写的合理性做检查。例如，<tt class="LITERAL">SET TIMEZONE TO FOOBAR0</tt> 不会报错，而是使系统使用 GMT 。</p></li>
</ul>
<p>完整的时区名与时区缩写在理论与实践之间存在差异：时区缩写总是代表一个相对于 UTC 的固定偏移量，然而大多数完整的时区名隐含着一个本地夏令时规则，因此就有可能有两个相对于 UTC 的不同偏移量。</p>
<p>总体而言，PostgreSQL 8.2 版本以后时区名在所有情况下都是大小写无关的。而之前的版本在某些情况下是大小写敏感的。</p>
<p>无论是完整的时区名还是时区缩写都不是硬连接进服务器的，它们都是从安装目录下的 <tt class="FILENAME">.../share/timezone/</tt> 和 <tt class="FILENAME">.../share/timezonesets/</tt> 配置文件中获取的(参见<a href="datetime-config-files.html">章B.3</a>)</p>
<p>可以在 <tt class="FILENAME">postgresql.conf</tt> 文件里设置 <a href="runtime-config-client.html#GUC-TIMEZONE">timezone</a> 配置参数，或者用任何其它在<a href="runtime-config.html">章17</a>描述的标准方法。除此之外，还有好几种特殊方法可以设置它：</p>
<ul>
<li><p>如果既没有在 <tt class="FILENAME">postgresql.conf</tt> 里也没有在命令行开关上声明 <tt class="VARNAME">timezone</tt> ，那么服务器将试图使用服务器主机上的 <tt class="ENVAR">TZ</tt> 环境变量作为服务器的缺省时区。如果 <tt class="ENVAR">TZ</tt> 没有定义或者是 PostgreSQL 不认识的时区名，那么服务器将试图通过检查 C 库函数 <tt class="LITERAL">localtime()</tt> 的行为来判断操作系统的缺省时区。缺省时区是按照最接近 PostgreSQL 的已知时区的原则来选择的。</p></li>
<li><p>使用 SQL 命令 <tt class="COMMAND">SET TIME ZONE</tt> 为会话设置时区，这是 <tt class="COMMAND">SET TIMEZONE TO</tt> 的一个可选的拼写方式，更加兼容标准。</p></li>
<li><p>如果在客户端设置了 <tt class="ENVAR">PGTZ</tt> 环境变量，那么 <span class="APPLICATION">libpq</span> 在连接时将使用这个环境变量给后端发送一个 <tt class="COMMAND">SET TIME ZONE</tt> 命令。</p></li>
</ul>
</div>
<div class="SECT2"><h2 class="SECT2"><a name="DATATYPE-DATETIME-INTERNALS">8.5.4. 内部</a></h2>
<p>PostgreSQL 使用儒略历法计算所有日期/时间，假设一年的长度是 365.2425 天。这个方法可以很精确地预计/计算从 4713 BC(公元前 4713 年)到很久的未来的任意一天的日期。</p>
<p>19 世纪以前的日期传统(历法)只是对一些趣味读物有意义，而在我们这里好像没有充分的理由把它们编码入日期/时间控制器里面去。</p>
</div>
</div>
<div>
<hr align="LEFT" width="100%">
<table summary="Footer navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><td width="33%" align="left" valign="top"><a href="datatype-binary.html" accesskey="P">后退</a></td><td width="34%" align="center" valign="top"><a href="index.html" accesskey="H">首页</a></td><td width="33%" align="right" valign="top"><a href="datatype-boolean.html" accesskey="N">前进</a></td></tr>
<tr><td width="33%" align="left" valign="top">二进制数据类型</td><td width="34%" align="center" valign="top"><a href="datatype.html" accesskey="U">上一级</a></td><td width="33%" align="right" valign="top">布尔类型</td></tr>
</table>
</div>
</body></html>